package DPOP;

import java.util.ArrayList;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.UnreadableException;

public class A2 extends Agent{
	String myName = new String();
	String rootName = "A1";
	String DFS = "";
	String p = new String();
	String[][] ngh = {{"A2", "3", "unvisited"},     // Neighbors {name, commCost, status}
					  {"A3", "1", "unvisited"}};
					  //{"A4", "4", "unvisited"}};
	int relation_A3A1[][]= {
			{0,0,5},
			{0,1,8},
			{1,0,20},
			{1,1,3}
	};

	int relation_A3A2[][]= {
			{0,0,5},
			{0,1,8},
			{1,0,20},
			{1,1,3}
	};

	int[][] join = new int[4][3];
	int[][] util = new int[4][3];
	final int UTIL = 1;
	final int VALUE = 2;
	
	ArrayList<String> c = new ArrayList<String>();  // Children
	ArrayList<String> pp = new ArrayList<String>(); // Pseudo Parent
	ArrayList<String> pc = new ArrayList<String>(); // Pseudo Children
	int DFSCount = 0;
	boolean DEBUG = true;
	private static final long serialVersionUID = 1L;
	ACLMessage rMsg;
	// Put agent initializations here
	protected void setup() 
	{
		// Printout a welcome message
		XMLReader reader = new XMLReader("..//A2.xml", true);
		myName = reader.getAgentName();
		System.out.println("Hello World! I am "+myName);
		reader.getNbConstraints();
		
		// how many total number of neighbor variables
		int nbRelatedVariables = 0;
		for (int i=0; i<reader.getNbNeighbors(); i++) {
			nbRelatedVariables = nbRelatedVariables + reader.getNeighbors()[i].getNbRelatedVariables();
		}
		
		ngh = new String[nbRelatedVariables][3];
		for (int i=0; i<reader.getNbNeighbors(); i++) {
			for ( int j=0; j<reader.getNeighbors()[i].getNbRelatedVariables(); j++) {
				ngh[i][0] = reader.getNeighbors()[i].getRelatedVariables()[j][0];
				ngh[i][1] = reader.getNeighbors()[i].getCommCost();
				ngh[i][2] = "unvisited";
			}
		}
		
		addBehaviour(new OneShotBehaviour()
		{
			private static final long serialVersionUID = 1L;

			public void action()
			{
				myPrint("Going into tokenpassing");
				tokenPassing();
				doDelete();

			}// End onTick


			public void tokenPassing() {
				//ACLMessage rMsg;
				if (myName.equals(rootName)) {
					myPrint("I am root");
					p = "";
					DFS  = "";
				}
				else {
					myPrint("Going inside handleIncomingTokens");
					DFS = handleIncomingTokens();
					myPrint("Returned DFS is "+DFS);
				}
				DFS = DFS + myName;
				myPrint("DFS after adding my name = "+DFS);
				// Sort neighbors list according to communication cost
				for (int a = 0; a < ( ngh.length - 1 ); a++) {
					for (int b = 0; b < ngh.length - a - 1; b++) {
						if (Integer.parseInt(ngh[b][1]) > Integer.parseInt(ngh[b+1][1])) {
							String[] swap = {ngh[b][0], ngh[b][1], ngh[b][2]};
							ngh[b][0]     = ngh[b+1][0];
							ngh[b][1]     = ngh[b+1][1];
							ngh[b][2]     = ngh[b+1][2];
							ngh[b+1][0]   = swap[0];
							ngh[b+1][1]   = swap[1];
							ngh[b+1][2]   = swap[2];
						} // end if
					} // end inner for
				} // end outer for
                myPrint("After Sorting--");
                for (int i = 0; i<ngh.length; i++) {
                	myPrint("\t"+ngh[i][0]+"\t"+ngh[i][1]+"\t"+ngh[i][2]);
                }
				// Iterate through all unvisited neighbors
				for (int i = 0; i<(ngh.length); i++){
					if (ngh[i][2].equals("unvisited")) {
						myPrint("Sending messages to my unvisited neighbor "+ngh[i][0]);
						// adding neighbor to child list
						if (!pp.contains(ngh[i][0]))
							c.add(ngh[i][0]);
						// Send DFS message to this child node
						ACLMessage sMsg = new ACLMessage(ACLMessage.INFORM);				
						sMsg.addReceiver(new AID(ngh[i][0], AID.ISLOCALNAME));
						sMsg.setContent(DFS);
						myPrint("Sending DFS ("+DFS+") to "+ngh[i][0]);
						send(sMsg);
						// wait for the child to return the message
						myPrint("waiting to receive back DFS message");
						//rMsg = blockingReceive();
						// if this rMsg is not from the child then it is from pseudo child
						do {
							rMsg = blockingReceive();
							if (!(rMsg.getSender().getLocalName().equals(ngh[i][0]))) {
								myPrint("Some other message received");
								String tempDFS = handleIncomingTokens();
								ACLMessage sMsg1 = new ACLMessage(ACLMessage.INFORM);
								myPrint("Sending DFS = "+tempDFS+" back to "+rMsg.getSender().getLocalName());
								sMsg1.addReceiver(rMsg.getSender());
								sMsg1.setContent(tempDFS);
								send(sMsg1);
							}
							else {
								myPrint("DFS returned from the child "+ngh[i][0]);
							}
						}while (!(rMsg.getSender().getLocalName().equals(ngh[i][0])));
						// receive the DFS message from the child and update DFS
						if (rMsg != null){
							if(rMsg.getSender().getLocalName().equals(ngh[i][0])){
								DFS = rMsg.getContent();
							}
						}
					} // end if
				} // end for
				/**
				 * DONE:
				 *   remove myName from DFS 
				 *   send DFS to its parent 
				 */
				// subtree of this agent has been completely explored
				// remove itself from the DFS and send it to its parent
				DFS  = DFS.replace(myName, "");
				ACLMessage sMsg = new ACLMessage(ACLMessage.INFORM);
				sMsg.addReceiver(new AID(p, AID.ISLOCALNAME));
				sMsg.setContent(DFS);
				send(sMsg);
			} // end procedure tokenPassing

			
		});// end oneShot behavior
		
	}// End setup
	
	String handleIncomingTokens() {
		/**
		 *  TODO:
		 *     receive ACLMessage
		 */
		String DFSl = new String();
		// if no messages have been received yet
		if (DFSCount == 0 && !myName.equals(rootName)) {
			myPrint("blocking receive");
			rMsg = blockingReceive();
		}
        // message received
		if (rMsg != null) {
			DFSCount++;
			DFSl = rMsg.getContent();
			myPrint("Received DFS is "+DFSl);
			// mark msg.sender() visited in the ngh array
			for (int i=0; i<ngh.length; i++) {
				if (ngh[i][0].equals(rMsg.getSender().getLocalName())) {
					myPrint("Marking sender visited "+ngh[i][0]);
					ngh[i][2] = "visited";
				}
			}

			// if first message
			if (DFSCount == 1 && !(myName.equals(rootName))) {
				p = rMsg.getSender().getLocalName();
				for (int i = 0; i<ngh.length; i++){
					// if DFSl contains a neighbors 
					//which is not my parent then it is my pseudo parent
					myPrint("DFSl = "+DFSl+"\tParent = "+p+"\tngh[i][0] = "+ngh[i][0]);
					myPrint("C1 = "+DFSl.contains(ngh[i][0]));
					myPrint("C2 = "+!(ngh[i][0].equals(p)));
					myPrint("C3 = "+(DFSl.contains(ngh[i][0]) && !(ngh[i][0].equals(p))));
					if (DFSl.contains(ngh[i][0]) && !(ngh[i][0].equals(p))) {
						myPrint("Adding Pseudo Parent "+ngh[i][0]);
						pp.add(ngh[i][0]);
						//ngh[i][2] = "visited";
					}
				}
			}
			else { // not my first message
				// Sort unvisited neighbors
				if (c.contains(rMsg.getSender().getLocalName()));
				else {
					myPrint("Adding Pseudo Child "+ rMsg.getSender().getLocalName());
					pc.add(rMsg.getSender().getLocalName());
					for (int i=0; i<ngh.length; i++) {
						if (ngh[i][0].equals(rMsg.getSender().getLocalName())) {
							ngh[i][2] = "visited";
						}
					}
				}
			}
		}
		myPrint("Returning DFS = "+DFSl);
		return DFSl;
	}
	
	public void runUTILPropogation() {
		// join all util messages from the children as they arrive
		for (int i=0; i<c.size(); i++) {
			ACLMessage m1 = blockingReceive();
			if ((c.contains(m1.getSender().getLocalName()) && (m1.getPerformative() == UTIL))) {
				int temp[][] = null;
				try {
					temp = (int[][]) m1.getContentObject();
				} catch (UnreadableException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				for (int j=0; j<3; j++) {
					for (int k=0; k<4; k++) 
						join[j][k] = join[j][k] + temp[j][k];	
				} // end for
			}// end if
		} // end for
		
		
		for (int i=0; i<3; i++) {
			for (int j=0; j<4; j++) 
				join[i][j] = join[i][j] + relation_A3A1[i][j];
		} // end for
		
		// join all relations with pseduoparent
		for (int k=0; k<pp.size(); k++) {
			for (int i=0; i<3; i++) {
				for (int j=0; j<4; j++) 
					join[i][j] = join[i][j] + relation_A3A1[i][j];
			} // end inner for
		} // end outer for
		
		// use projection to eliminate self
		
		// send this util message to parent
	}
	
	public void runVALUEPropogation() {
		ACLMessage m1 = null;
		if (!myName.equals(rootName)){
			 m1 = blockingReceive();
		}
		// pick your best value
		int myVal = Integer.parseInt(m1.getContent());
		
		// send this value to all your children
		ACLMessage m2 = new ACLMessage(VALUE);
		m2.setContent(Integer.toString(myVal));
		m2.setPerformative(VALUE);
		for (String child:c)
			m2.setSender(new AID(child, AID.ISLOCALNAME));
	}
	public void myPrint(String s) {
		if (DEBUG) 
			System.out.println(this.getLocalName()+": "+s+"");
	}
	public Agent getAgent(){ return this;}

	// To terminate the client agent
	public void takeDown() {
		String s = "\n-----------------------------\n";
			   s = s + this.getLocalName() + " is terminating!";
			   s = s + "\n\tParent: "+p;
			   s = s + "\n\tChildren: ";
		for (String temp:c) {
			s =  s + temp + ", ";
		}
		s = s + "\n\tPseudo Parents: ";
		for (String temp:pp) {
			s = s + temp + ", ";
		}
		s = s + "\n\tPseudo Children: ";
		for (String temp:pc) {
			s = s + temp + ", ";
		}
		System.out.println(s +"\n-----------------------------\n");
	}
}
